/*
 *  Copyright 2010 UPF Telecos 4th group G.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */

package edu.upf.core;

import edu.upf.messages.GenericMessage;
import edu.upf.messages.UserContent;
import edu.upf.utils.LogSystem;
import edu.upf.network.impl.XMPPClient;
import edu.upf.content.DescriptionContent;
import edu.upf.messages.SearchResult;
import edu.upf.messages.SearchProfile;
import edu.upf.userinterface.UserChat;
import edu.upf.userinterface.UserInterface;
import edu.upf.userinterface.impl.GraphicChat;
import edu.upf.userinterface.impl.GraphicInterface;
import edu.upf.userinterface.impl.TextChat;
import edu.upf.userinterface.impl.TextInterface;
import edu.upf.utils.Definitions;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.swing.UIManager;
import org.jivesoftware.smack.packet.Message;



/**
 * Class that handles all the interactions between objects, the controller class
 * in MVC
 * @author netjois
 */
public class ClientLogic implements Definitions {

    private XMPPClient jabberClient;
    private UserInterface userInterface;
    private DatabaseManager databaseManager;
    private String username;
    private List<String> buddyList;
    private List<String> onlineUsers;
    private List<DescriptionContent> contentList;
    private Map<String,UserChat> currentChatWindows;
    private LogSystem log;
    private boolean guiMode;


    /**
     * Constructor of the ClientLogic, depending on the parameter initializes a
     * text or graphical user interface
     * @param activateGUI 
     */
    public ClientLogic(boolean activateGUI){
        log=new LogSystem(ClientLogic.class);
        jabberClient=new XMPPClient(this);
        buddyList=new ArrayList();
        onlineUsers=new ArrayList();
        contentList = new ArrayList();
        currentChatWindows = new HashMap();
        databaseManager = new DatabaseManager(this);
        guiMode=activateGUI;
        if(guiMode) {
            initGraphicInterface();
        }else{
            initTextInterface();
        }
    }

    /**
     * Sets the system look and feel for the swing GUI
     */
    private void setSystemLookandFeel(){
        try{
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            log.addDebugLog("System look and feel set");
            //UIManager.setLookAndFeel("net.sourceforge.napkinlaf.NapkinLookAndFeel");
        }catch(Exception e){log.addDebugLog("Failed to set look and feel"+e.getMessage());}
    }
    
    /**
     * Inits the graphic java swing interface
     */
    private void initGraphicInterface(){
        setSystemLookandFeel();
        userInterface=new GraphicInterface(this);
        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                ((GraphicInterface)userInterface).setVisible(true);
            }
        });
    }

    private void initTextInterface(){
        userInterface=new TextInterface();
    }

    /**
     * Logs the user onto the server with the specified resource
     * @param user
     * @param pass
     * @param resource
     * @return
     */
    public boolean login(String user, String pass, String resource) {
        username = user;
        addArrobaServer();
        return jabberClient.login(username, pass, resource);
    }

    /**
     * Logouts the user from the server
     * @return
     */
    public boolean logout(){
        return jabberClient.logout();
    }

    /**
     * Adds the server extension to a username introduced without it
     */
    private void addArrobaServer(){
        if(username.indexOf('@')==-1){
            username+=SERVER_EXTENSION;
        }
    }
    
    /**
     * Adds a buddy to the buddy list
     * @param user
     */
    public void addBuddyUser(String user){
        buddyList.add(user);
        userInterface.addUserToList(user);
    }

    /**
     * Adds a user to the list of online users
     * @param user
     */
    public void addOnlineUser(String user){
        onlineUsers.add(user);
        eventLog("User: "+user+" online");
    }

    /**
     * Removes a user from the list of online users
     * @param user
     */
    public void removeOnlineUser(String user){
        onlineUsers.remove(user);
        eventLog("User: "+user+" offline");
    }

    /**
     * Returns the contact direction of a user eliminating the resource
     * @param direc
     * @return
     */
    public String getFilteredUser(String direc){
        if(direc.indexOf('/')!=-1){
            return direc.substring(0,direc.indexOf('/'));
        }
        else{
            return direc;
        }
    }

    /**
     * Returns the name of the user logged in
     * @return
     */
    public String getUserName(){
        return username;
    }

    /**
     * Returns the username of the contact which is in the parameter position on
     * the roster list. Is the text that a user introduces on the client for
     * logging in.
     * @param index
     * @return
     */
    public String getContact(int index){
        return (jabberClient.getRoster().getEntry(buddyList.get(index))).getUser();
    }

    /**
     * Returns the mail of the contact which is in the parameter position on the
     * roster list
     * @param index
     * @return
     */
    public String getUserMail(int index){
        return getFilteredUser((jabberClient.getRoster().getEntry(buddyList.get(index))).getUser());
    }

    /**
     * Returns the current showed name of the contact given by the position
     * index on the roster.
     * @param index
     * @return
     */
    public String getBuddyName(int index){
        return buddyList.get(index);
    }


    /**
     * Processes a received chat message
     * @param msg
     */
    public void processChatMessage(Message msg){
        String buddy=getFilteredUser(msg.getFrom());
        openUserChat(buddy).addText(msg.getBody());
        log.addDebugLog("Received chat msg from: "+buddy+", Text: "+msg.getBody());
    }

    /**
     * Processes and incoming search query or profile
     * @param dest
     * @param query
     */
    public void processSearchQuery(String dest, SearchProfile query){
        SearchResult result = databaseManager.getQueryResults(query.getQueryType(),query.getQuery());
        log.addDebugLog("Search query received: "+query.getQueryType()+", "+query.getQuery());
        jabberClient.sendGenericMessage(new GenericMessage(username,dest,result));
    }

    /**
     * Processes the received results associated to a search
     * @param orig
     * @param result
     */
    public void processSearchResult(String orig, SearchResult result){
        log.addDebugLog("Received search results");
        for(int i=0; i<result.contentLength();i++){
            DescriptionContent newContent = result.getContent(i);
            userInterface.addResult(newContent);
            contentList.add(newContent);
        }
    }

    /**
     * Sends a search profile to all available contacts. True if the send was
     * succesful. It does not verify that the receivers got the message.
     * @param type
     * @param query
     * @return
     */
    public boolean sendSearchQuery(String type, String query) {

        log.addDebugLog("Sending SearchProfile: " + query);
        SearchProfile searchProfile = new SearchProfile(type, query);

            log.addDebugLog("Sending message to1:");
            jabberClient.addSearchResultListener(query);

            //Clear previous results shown
            contentList.clear();
            log.addDebugLog("Sending message to2:");

            String nextContact;
            for (Iterator<String> i = onlineUsers.iterator(); i.hasNext();) {
                nextContact = i.next();
                log.addDebugLog("Sending message to3:"+nextContact);
                jabberClient.sendGenericMessage(new GenericMessage(username,nextContact,searchProfile));
            }
        return true;
    }

    /**
     * Sends a chat message to the given destination
     * @param dest
     * @param text
     * @return
     */
    public boolean sendChatMessage(String dest, String text){
        jabberClient.sendChatMessage(dest, text);
        log.addDebugLog("Sending message: "+text+", to: "+dest);
        return true;
    }

    /**
     * Creates a chat session with the given buddy
     * @param buddy
     * @return
     */
    public UserChat openUserChat(String buddy){
   
        UserChat currentChat = currentChatWindows.get(buddy);
        if (currentChat==null){
            log.addDebugLog("Creating chat with buddy "+buddy);
            if(guiMode){
                currentChat=new GraphicChat(buddy, this);
                ((GraphicChat)currentChat).setVisible(true);
            }else{
                currentChat=new TextChat();
            }
        }
        currentChatWindows.put(buddy,currentChat);
        return currentChat;
    }

    /**
     * Close the chat session with the given buddy
     * @param buddy
     * @return
     */
    public boolean closeChat(String buddy){
        currentChatWindows.remove(buddy);
        log.addDebugLog("Deleting chat with buddy "+buddy);
        return true;
    }

    /**
     * Changes the current status of a user to a available presence or not
     * @param user
     * @param present
     */
    public void updateUserPresence(String user, boolean present){
        log.addDebugLog("Updating user "+user+" presence, present="+present);
        if(present){
            addOnlineUser(user);
        }
        else{
            removeOnlineUser(user);
        }
    }

    /**
     * Shows a log event (i.e. new buddy available) on the user space log system
     * @param logEntry
     */
    public void eventLog(String logEntry){
        log.addDebugLog("Adding to the uintf log: "+logEntry);
        userInterface.userLog(logEntry);
    }

    /**
     * Initiates the download of the selected content using the identification
     * number associated to the list of search result contents showed.
     * @param id
     * @return
     */
    public boolean downloadContent(int id) {
        DescriptionContent selectedContent=contentList.get(id);
        String sourceID=selectedContent.getPath()+selectedContent.getName();
        UserContent userContent=new UserContent(sourceID);
        GenericMessage msg = new GenericMessage(username,selectedContent.getOwner(),userContent);
        jabberClient.sendGenericMessage(msg);
        jabberClient.addTransferListener(sourceID);
        return true;
    }

    /**
     * Processes a received User Content request from the given client
     * @param orig
     * @param payload
     */
    public void processUserContent(String orig, UserContent payload) {
        try {
            log.addDebugLog("Sending media with source: " + payload.getSourceID());
            File media = new File(payload.getSourceID());
            log.addDebugLog("Absolute path: " + media.getAbsolutePath());
            log.addDebugLog("Canonical path: " + media.getCanonicalPath());
            log.addDebugLog("File exists: " + media.exists());
            jabberClient.sendMedia(media, orig, NAME);
        } catch (IOException ex) {
            log.addDebugLog(ex.getMessage());
        }
    }

    /**
     * Shows information about the media received
     * @param description
     */
    public void mediaReceived(String description){
        log.addDebugLog("New file received:" + description);
    }
}
